package com.demo.atomic;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAdder;

//当竞争激烈的时候，怎么进一步提升性能？一种基本方案就是可以使用热点隔离，将竞争的数据进行分解，基于这个思路，虽然在cas中没有锁，但是像减小锁粒度这种分离热点的思想依然可以使用，
//一种可行的方案就是仿照ConcurrentHashMap，将热点数据分离。比如，可以将AtomicInteger的内部核心数据value分离成一个数组，每个线程访问时，通过哈希算法映射到一个数字进行计算，
//而最终的计数结果，则为这个数组的求和累加，其中，热点数据value被分离成多个单元cell，每个cell独自维护内部的值，当前对象的实际值由所有cell累计合成，这样，热点就进行了有效的分离，
//提高了并行度。

//比较LongAdder、锁、cas
public class LongAdderDemo {

	private static final int MAX_THREAD = 3;//线程数
	private static final int TASK_COUNT = 3;//任务数
	private static final int TARGET_COUNT = 10000000;//目标总数

	private AtomicLong acount = new AtomicLong(0l);
	private LongAdder lacount = new LongAdder();
	private long count = 0;

	static CountDownLatch cdlsync = new CountDownLatch(TASK_COUNT);
	static CountDownLatch cdlatomic = new CountDownLatch(TASK_COUNT);
	static CountDownLatch cdladdr = new CountDownLatch(TASK_COUNT);

	//有锁的加法
	protected synchronized long inc() {
		return ++count;
	}

	//有锁的操作
	protected synchronized long getCount() {
		return count;
	}

	public class SyncThread implements Runnable{
		protected String name;
		protected long starttime;
		LongAdderDemo out;

		public SyncThread(LongAdderDemo o, long starttime) {
			out = o;
			this.starttime = starttime;
		}

		@Override
		public void run() {
			long v = out.getCount();
			while(v < TARGET_COUNT){
				v = out.inc();
			}
			long endtime = System.currentTimeMillis();
			System.out.println("SyncThread spend:" + (endtime-starttime) + "ms" + " v=" + v);
			cdlsync.countDown();
		}
	}

	public void testSync() throws Exception {
		ExecutorService exe = Executors.newFixedThreadPool(MAX_THREAD);
		long starttime = System.currentTimeMillis();
		SyncThread sync = new SyncThread(this, starttime);
		for(int i=0; i<TASK_COUNT; i++){
			exe.submit(sync);
		}
		cdlsync.await();
		exe.shutdown();
	}

	public class AtomicThread implements Runnable{
		protected String name;
		protected long starttime;

		public AtomicThread(long starttime) {
			this.starttime = starttime;
		}

		@Override
		public void run() {
			long v = acount.get();
			while(v < TARGET_COUNT){
				v = acount.incrementAndGet();
			}
			long endtime = System.currentTimeMillis();
			System.out.println("AtomicThread spend:" + (endtime-starttime) + "ms" + " v=" + v);
			cdlatomic.countDown();
		}
	}

	public void testAtomic() throws Exception {
		ExecutorService exe = Executors.newFixedThreadPool(MAX_THREAD);
		long starttime = System.currentTimeMillis();
		AtomicThread sync = new AtomicThread(starttime);
		for(int i=0; i<TASK_COUNT; i++){
			exe.submit(sync);
		}
		cdlatomic.await();
		exe.shutdown();
	}

	public class LongAddrThread implements Runnable{
		protected String name;
		protected long starttime;

		public LongAddrThread(long starttime) {
			this.starttime = starttime;
		}

		@Override
		public void run() {
			long v = lacount.sum();
			while(v < TARGET_COUNT){
				//LongAddr中，单个数值被分解为多个不同的段，因此，在进行累加后，increment函数并不能返回当前的数值，要取得当前的实际值，需要使用sum函数重新计算。
				lacount.increment();
				v = lacount.sum();
			}
			long endtime = System.currentTimeMillis();
			System.out.println("LongAddrThread spend:" + (endtime-starttime) + "ms" + " v=" + v);
			cdladdr.countDown();
		}
	}

	public void testAtomicLong() throws Exception {
		ExecutorService exe = Executors.newFixedThreadPool(MAX_THREAD);
		long starttime = System.currentTimeMillis();
		LongAddrThread sync = new LongAddrThread(starttime);
		for(int i=0; i<TASK_COUNT; i++){
			exe.submit(sync);
		}
		cdladdr.await();
		exe.shutdown();
	}

	public static void main(String[] args) throws Exception {
		LongAdderDemo d = new LongAdderDemo();
		d.testSync();
		d.testAtomic();
		d.testAtomicLong();
	}

}
